// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: tianqiang

#pragma once

#include "coin2/exchange/base/api_util/api_signature.h"

class MaicoinWsAuth {
 public:
  explicit MaicoinWsAuth(const std::string& key_filepath) : key_(AuthKey::FromFile(key_filepath)) {}

  const std::string CreateSignature() const {
    int64_t nonce = GetCurrentTimestamp() / 1000000;

    std::string json =
        R"({{"action":"auth","apiKey":"{}","nonce":{},"signature":"{}","id":"client-id"}})";

    std::string signature =
        ApiSignatureUtil::GetSignatureByHexHmacSHA256(std::to_string(nonce), key_.secret_key());

    return fmt::format(json, key_.access_key(), nonce, signature);
  }

 protected:
  AuthKey key_;
};

class MaicoinRestAuth : public BaseRestAuth {
 public:
  explicit MaicoinRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      const std::string& port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)), host_(host), port_(port), bind_ip_(bind_ip) {
    header_.emplace_back("Content-Type", "application/json");
    header_.emplace_back("X-MAX-ACCESSKEY", key_.access_key());
    header_.emplace_back("X-MAX-PAYLOAD", "");
    header_.emplace_back("X-MAX-SIGNATURE", "");
  }

  explicit MaicoinRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      int32_t port,
      const std::string& bind_ip)
      : MaicoinRestAuth(key_filepath, host, std::to_string(port), bind_ip) {}

  AsioHttpContext* CreateRequestByGetWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    GenSignature(url_path, builder);

    std::string target = url_path + "?" + builder->ComputePostBodyAsQueryString();

    return AsioHttpContext::NewGet(host_, port_, bind_ip_, target, header_);
  }

  AsioHttpContext* CreateRequestByPostWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    GenSignature(url_path, builder);

    return AsioHttpContext::NewPost(
        host_,
        port_,
        bind_ip_,
        url_path,
        header_,
        builder->ComputePostBody());
  }

  AsioHttpContext* CreateRequestByDeleteWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

  AsioHttpContext* CreateRequestByPutWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

 protected:
  AuthKey key_;
  std::string host_;
  std::string port_;
  std::string bind_ip_;
  int64_t prev_nonce_ = 0;
  std::vector<std::pair<std::string, std::string>> header_;

 private:
  int64_t GetNonce() {
    prev_nonce_ = std::max(GetCurrentTimestamp() / 1000000, prev_nonce_ + 1);

    return prev_nonce_;
  }

  void GenSignature(const std::string& url_path, UrlParamsBuilder* builder) {
    std::string query_string = builder->ComputeQueryString();
    CHECK(query_string.empty());

    builder->PutPost<true>("nonce", GetNonce());
    builder->PutPost("path", url_path);

    std::string post_body = builder->ComputePostBody();
    std::string payload = impl::base64_encode(
        reinterpret_cast<const unsigned char*>(post_body.c_str()),
        post_body.length());
    std::string sign = ApiSignatureUtil::GetSignatureByHexHmacSHA256(payload, key_.secret_key());

    header_[2].second = payload;
    header_[3].second = sign;
  }
};
